import * as YvanUI from './YvanUIExtend'
import CtlGridLocale from './CtlGridLocale'
import { CtlGridPage } from './CtlGridPage'
import agGrid from 'ag-grid'
import CtlGridIdRender from './CtlGridIdRender'
import {
  YvGridColumnEditProp,
  YvGridColumnProp,
  YvGridProp
} from './CtlGridDefault'
import CtlGridCellCheckbox from './CtlGridCellCheckbox'
import CtlGridHeadCheckbox from './CtlGridHeadCheckbox'
import { CtlBase } from './CtlBase'
import { parseYvanPropChangeVJson } from './CtlUtils'
import { GridDataSource } from './YvanDataSourceGrid'
import { YvEvent, YvEventDispatch, YvEventDispatchArgs } from './YvanEvent'
import { YvanDataSourceGrid } from './YvanDataSourceGridImp'
import { CtlGridDefault } from './CtlDefaultValue' 
import CtlGridCellButton from './CtlGridCellButton'
import CtlGridFilterSet from './CtlGridFilterSet'
import CtlGridEditorText from './CtlGridEditorText'
import CtlGridEditorNumber from './CtlGridEditorNumber'
import CtlGridEditorCombo from './CtlGridEditorCombo'
import CtlGridEditorDate from './CtlGridEditorDate'
import CtlGridEditorSearch from './CtlGridEditorSearch'
import CtlGridCellImage from './CtlGridCellImage'
import { YvDataSource } from './YvanDataSourceImp'
import webix from 'webix'

/** 在CtlGridPage.ts 里面有关于点击分页按钮时候设置 GridRefreshMode **/
export enum GridRefreshMode {
  refreshRows,
  refreshWithFilter,
  refreshAndClearFilter
}

/**
 * 表格中的行按钮被点击后触发的事件参数
 */
export interface CtlGridRowButtonClickArgs {
  /**
   * 行数据
   */
  data: any

  /**
   * 行序号
   */
  rowIndex: number

  /**
   * 列名称
   */
  colId: string
}

/**
 * 扩展 grid 组件
 */
webix.protoUI(
  {
    name: 'grid',
    $init: function (config: any) {
      this._domid = webix.uid();
      this.$view.innerHTML = `<div id='${this._domid}' role="yvGrid" class="ag-theme-blue"></div>`
      _.extend(this.config, config)
      if (config.on && typeof config.on.onMyRender === 'function') {
        config.on.onMyRender.call(this)
      }
      if (!config.css) {
        config.css = 'webix_yv_grid';
      } else {
        config.css = config.css + ' webix_yv_grid';
      }
      if (typeof config.height === 'number') {
        this._viewobj.style.height = config.height
      }
      this._viewobj.style.minHeight='133px'
    }
  },
  webix.ui.view
)

export class CtlGrid extends CtlBase<CtlGrid> {
  static create(module: any, vjson: any): CtlGrid {
    const that = new CtlGrid(_.cloneDeep(vjson))
    that._module = module

    _.defaultsDeep(vjson, CtlGridDefault)

    if (vjson.hasOwnProperty('debugger')) {
      debugger
    }

    // 提取基础属性 onRender / ctlName / entityName 等等
    const yvanProp = parseYvanPropChangeVJson(vjson, [
      'height',
      'rowHeight',
      'headerHeight'
    ])

    // 将 yvanProp 合并至当前 CtlBase 对象
    _.assign(that, yvanProp)

    // 删除 vjson 所有数据, 替换为 template 语法
    _.forOwn(vjson, (value, key) => {
      delete vjson[key]
    })
    _.merge(vjson, {
      view: 'grid',
      // template: `<div role="yvGrid" class="ag-theme-blue"></div>`,
      height: yvanProp.height,
      on: {
        onMyRender: function (this: any) {
          _.defer(() => {
            that.attachHandle(this, { ...vjson, ...yvanProp })
            that._resetGrid()
          });
        },
        onDestruct(this: any) {
          if (that.gridApi) {
            that.gridApi.destroy()
            that.gridApi = undefined
          }
          that.removeHandle()
        }
      }
    })
    if (that.vjson.id) {
      vjson.id = that.vjson.id
    }
    return that
  }

  /*============================ 公共属性部分 ============================*/
  allCheckedData: any[] = []
  gridApi: any
  columnApi: any
  refreshMode: GridRefreshMode = GridRefreshMode.refreshRows;

  get webix() {
    return this._webix
  }

  /**
   * 设置数据源
   */
  set dataSource(nv: GridDataSource) {
    this.vjson.dataSource = nv
    // if (this._module.loadFinished) {
    //   throw new Error('Grid 不允许动态设置数据源')
    // }
  }

  /**
   * 获取数据源
   */
  get dataSource(): GridDataSource {
    return this.vjson.dataSource
  }

  /**
   * 表格内换行
   */
  readonly wrapText: boolean = false

  /**
   * 行高
   */
  readonly rowHeight: number = 33

  /**
   * 头高
   */
  readonly headerHeight: number = 35

  /**
   * 是否为客户端过滤
   */
  readonly clientFilter: boolean = false

  /**
   * 是否为客户端排序
   */
  readonly clientSort: boolean = false

  /**
   * 初始化时的数据
   */
  readonly data?: any[]

  /**
   * 是否允许编辑
   */
  readonly editable!: boolean

  /**
   * 允许勾选
   */
  readonly checkbox!: boolean

  /**
   * 允许跨页勾选
   */
  readonly useCrossPageCheck!: boolean

  /**
   * 单击后立刻进入编辑
   */
  readonly editSingleClick!: boolean

  /**
   * 允许添加
   */
  readonly allowNewRow!: boolean

  /**
   * 允许过滤
   */
  readonly filterable!: boolean

  /**
   * 显示序号
   */
  readonly showRowNumber!: boolean

  /**
   * 自动调整列宽
   */
  readonly autoSizeColumns!: boolean

  /**
   * 单元格选中模式
   */
  readonly allowCellSelection!: boolean

  /**
   * 行选中模式
   */
  readonly allowRowSelection!: boolean

  /**
   * ID字段
   */
  readonly idField!: string

  /**
   * 值分隔符
   */
  readonly valueSep!: string

  /**
   * 保存时机(换行时/编辑完后)
   */
  readonly saveOn!: 'editFinish' | 'rowChanged'

  /**
   * 新添加行时，初始化的数据
   */
  readonly newRowData!: any

  /**
   * 失去焦点后立刻结束编辑
   */
  readonly stopEditingWhenGridLosesFocus!: boolean

  /**
   * 是否默认选中行
   */
  readonly defaultUnselecteRow: boolean = false
  

  /**
   * 列定义
   */
  public columns!: any[] | undefined

  /**
   * 二纬表头
   */
  readonly columnGroup!: any[] | undefined

  /**
   * 是否自动读取数据
   */
  readonly autoLoad: boolean = true

  /**
   * 是否分页
   */
  readonly pagination: boolean = false

  /**
   * 分页大小
   */
  readonly pageSize: number = 100

  public gridPage!: CtlGridPage

  /**
   * 行被选中后触发
   */
  onRowSelect?: YvEvent<CtlGrid, any>

  /**
   * 行被选中后触发
   */
  onRowCheck?: YvEvent<CtlGrid, any>

  /**
   * 行被双击后触发
   */
  onRowDblClick?: YvEvent<CtlGrid, any>

  /**
   * 勾选项改变后触发
   */
  onCheckedChange?: YvEvent<CtlGrid, any>

  /**
   * 数据绑定完毕后触发
   */
  onBindingComplete?: YvEvent<CtlGrid, any>

  /**
   * 焦点单元格发生变化后触发
   */
  onCellFocused?: YvEvent<CtlGrid, any>

  /**
   * 单元格被点击后触发
   */
  onCellClicked?: YvEvent<CtlGrid, any>

  /**
   * 行样式计算
   */
  onRowStyle?: YvEvent<CtlGrid, any>

  /**
   * 第一行有效数据被渲染后触发
   */
  onFirstDataRendered?: YvEvent<CtlGrid, any>

  /**
   * 按下键后触发
   */
  onKeyDown?: YvEvent<CtlGrid, any>

  /**
   * 行被删除的回调事件
   * @param condition 
   */
  onRowDelete?: YvEvent<CtlGrid, any>
  /**
   * 获取被勾选的行ID集合
   */
  // checkedIds: any[] = []

  /**
   * 选择一个指定行
   * @param condition 可以是 key(主键), 也可以是 function 条件, 匹配到符合 condition 的第一条记录
   */
  selectRow(condition: GridSelectCondition) {
    const node: any = this._findNode(condition)
    if (node) {
      this.gridApi.setFocusedCell(node.rowIndex, '__ID__')
      node.setSelected(true)
      return true
    }
    return false
  }

  /**
   * 选择一个指定行
   * @param condition 可以是 key(主键), 也可以是 function 条件, 匹配到符合 condition 的第一条记录
   */
  singleSelectRow(condition: GridSelectCondition) {
    const node: any = this._findNode(condition)
    if (node) {
      node.setSelected(true)
      return true
    }
    return false
  }

  /**
   * 选择一个指定行
   * @param condition 
   */
  selectRowWithCell(condition: GridSelectCondition, cellField: string) {
    const node: any = this._findNode(condition)
    if (node) {
      this.gridApi.setFocusedCell(node.rowIndex, cellField)
      node.setSelected(true)
      return true
    }
    return false
  }

  /**
   * 闪烁指定行
   * @param condition 可以是 key(主键), 也可以是 function 条件, 匹配到符合 condition 的第一条记录
   */
  flashRow(condition: any) {
    const node = this._findNode(condition)
    if (node) {
      this.gridApi.flashCells({ rowNodes: [node] })
      return true
    }
    return false
  }

  /**
   * 闪烁指定单元格
   * @param cols 列数组
   * @param condition 可以是 key(主键), 也可以是 function 条件, 匹配到符合 condition 的第一条记录
   */
  flashCell(cols: any, condition: any) {
    const node = this._findNode(condition)
    if (node) {
      this.gridApi.flashCells({ columns: cols, rowNodes: [node] })
      return true
    }
    return false
  }

  _validateRowChecked(data: any[]) {
    _.forEach(data, item => {
      _.forEach(this.allCheckedData, ii => {
        if (this._getIdByRow(item) === this._getIdByRow(ii)) {
          item.__grid_row_checked = true
        }
      })
    })
  }

  /**
   * 无感知刷新
   */
  _transactionUpdate(targetDataList: any[]) {

    if (this.useCrossPageCheck) {
      this._validateRowChecked(targetDataList);
    }

    if (this.editable) {
      const newRow = _.assign({
        __grid_row_new: true
      }, this.vjson.defautProperties)
      targetDataList.push(newRow)
      const maxLength: any = _.max([this.pageSize, targetDataList.length])
      this.paginationSetPageSize(maxLength + 1)
    }

    this._gridData = targetDataList;

    if (this.refreshMode === GridRefreshMode.refreshWithFilter || this.refreshMode === GridRefreshMode.refreshAndClearFilter) {

      /** 更改当前的刷新模式， 避免重复刷新 **/
      this.refreshMode = GridRefreshMode.refreshRows
      this.setData(targetDataList);
      if (this.dataSourceBind) {
        this.gridApi.setFilterModel(this.dataSourceBind.lastFilterModel)
      }
    } else {
      this._transactionUpdateRow(targetDataList)
    }
  }

  /**
   * 
   * @param targetDataList 
   * @param deleteIndex
   */
  _deleteToUpdate(targetDataList: any[], deleteIndex: number) {
    this._gridData.splice(deleteIndex, 1)

    if (this.useCrossPageCheck) {
      this._validateRowChecked(targetDataList)
    }

    if (targetDataList.length <= 0) {
      const newRow = _.assign({
        __grid_row_new: true
      }, this.vjson.defautProperties)
      targetDataList.push(newRow)
      const maxLength: any = _.max([this.pageSize, targetDataList.length])
      this.paginationSetPageSize(maxLength + 1)
    }

    this._gridData = targetDataList;

    if (this.refreshMode === GridRefreshMode.refreshWithFilter || this.refreshMode === GridRefreshMode.refreshAndClearFilter) {

      /** 更改当前的刷新模式， 避免重复刷新 **/
      this.refreshMode = GridRefreshMode.refreshRows
      this.setData(targetDataList);
      if (this.dataSourceBind) {
        this.gridApi.setFilterModel(this.dataSourceBind.lastFilterModel)
      }
    } else {
      this._transactionUpdateRow(targetDataList)
    }
    if (deleteIndex < this._gridData.length) {
      this.selectRow(node => node.rowIndex === deleteIndex);
    }
    else {
      this.selectRow(node => node.rowIndex === this._gridData.length - 1)
    }
  }

  public updateRowData(rowData: any[]) {
    const transaction = {
      update: rowData
    }
    this.gridApi.updateRowData(transaction);
  }

  /**
   * 刷新行数据
   */
  _transactionUpdateRow(targetDataList: any[]) {
    /** 更改当前的刷新模式， 避免重复刷新 **/
    this.refreshMode = GridRefreshMode.refreshRows
    const transaction: any = {
      add: [],
      remove: [],
      update: []
    }
    let i = 0
    this.gridApi.forEachNode((node: any) => {
      if (i === targetDataList.length) {
        //已经越位
        transaction.remove.push(node.data)
      } else {
        const newData = targetDataList[i++]
        node.setData(newData)
        transaction.update.push(node.data)
      }
    })
    for (; i < targetDataList.length; i++) {
      transaction.add.push(targetDataList[i])
    }
    this.gridApi.updateRowData(transaction)
  }

  public addNewEditRow() {
    const newRow = _.assign({
      __grid_row_new: true
    }, this.vjson.defautProperties);

    const transaction: any = {
      add: [newRow],
      addIndex: this.gridApi.getDisplayedRowCount(),
    }
    this.gridApi.updateRowData(transaction);
  }

  /**
   * 获取全部数据
   */
  getData(): any[] | undefined {
    const result: any[] = []
    this.gridApi.forEachNode((node: any) => {
      result.push(node.data)
    })
    return result
  }

  /**
   * 为表格设置数据
   * 注意，调用此方法，必须在初始化时，给一个空的 data: [] 属性
   * 确保表格 rowModelType:clientSide 模式
   */
  setData(nv: any[] | undefined) {
    this.gridApi.setRowData(nv)
  }

  /**
   * 无感刷新
   * 清空缓存，从后台重新拉取数据，表格上临时修改的内容都会被清空
   *
   * option:
   *   clearFilter=true 是否清空筛选
   */
  // reload(option?: any) {
  //   this.loading = true
  //
  //   //无感刷新之前，清空所有状态
  //   this._clearCache()
  //
  //   //需要重新请求 rowCount(总数据行)
  //   if (this.dataSourceBind) {
  //     this.dataSourceBind.clearRowCount()
  //   }
  //
  //   if (this.entityName) {
  //     _.set(this.getModule(), this.entityName + '.selectedRow', undefined)
  //   }
  //
  //   /** 有clearFilter 参数的时候 一定刷新数据 **/
  //   if (option && option.clearFilter === true) {
  //     this.pageAbleDataRefreshMode = "refreshAndReset"
  //     this.gridApi.setFilterModel(null)
  //     if (this.dataSourceBind) {
  //       /** 表头筛选数据没有变化也要重新加载数据 **/
  //       if (_.isEqual(this.gridApi.getFilterModel(), this.dataSourceBind.lastFilterModel)) {
  //         this._filterChanged();
  //       }
  //     }
  //   } else {
  //     if (this.pagination) {
  //       this.pageAbleDataRefreshMode = "refreshWithFilter"
  //       this.gridPage.refreshGrid()
  //     } else {
  //       this.pageAbleDataRefreshMode = "refreshRows"
  //       this.gridApi.refreshInfiniteCache()
  //     }
  //   }
  // }

  /**
   * 隐藏某列
   */
  hideColumnsWithColIds(...cols: any[]) {
    this.columnApi.setColumnsVisible(cols, false)
  }

  /**
   * 显示某列
   */
  showColumnsWithColIds(...cols: any[]) {
    this.columnApi.setColumnsVisible(cols, true)
  }

  /**
   * 无感刷新
   * 清空缓存，从后台重新拉取数据，表格上临时修改的内容都会被清空
   *
   * option:
   *   clearFilter=true 是否清空筛选
   */
  reload(refreshMode: GridRefreshMode = GridRefreshMode.refreshWithFilter) {
    this.loading = true
    this.refreshMode = refreshMode
    //无感刷新之前，清空所有状态
    this._clearCache()

    //需要重新请求 rowCount(总数据行)
    if (this.dataSourceBind) {
      this.dataSourceBind.clearRowCount()
    }

    if (this.entityName) {
      _.set(this.getModule(), this.entityName + '.selectedRow', undefined)
    }

    /** 有clearFilter 参数的时候 一定刷新数据 **/
    if (refreshMode === GridRefreshMode.refreshAndClearFilter) {
      this.gridApi.setFilterModel(null)
      if (this.dataSourceBind) {
        /** 表头筛选数据没有变化也要重新加载数据 **/
        // if (_.isEqual(this.gridApi.getFilterModel(), this.dataSourceBind.lastFilterModel)) {
        this._filterChanged();
        // }
      }
    } else {
      if (this.pagination) {
        this.gridPage.refreshGrid()
      } else {
        this.gridApi.refreshInfiniteCache()
      }
    }
  }

  /**
   * 获取被选中的行主键
   */
  getSelectedId() {
    const row = this.getSelectedRow()
    if (row) {
      return this._getIdByRow(row)
    }
  }

  /**
   * 设置勾选的数据行集合
   */
  setCheckedIds(ids: Array<any>) {
    // 清空所有元素
    // this.checkedIds = ids;
    if (this.useCrossPageCheck) {
      this.allCheckedData = []
    }

    this.gridApi.forEachNode((node: any) => {
      if (_.indexOf(ids, this._getIdByRow(node.data)) >= 0) {
        node.data.__grid_row_checked = true
        if (this.useCrossPageCheck) {
          this.allCheckedData.push(node.data);
        }
      }
      else {
        node.data.__grid_row_checked = false
      }
    })

    // 刷新勾选单元格
    this.gridApi.refreshCells({
      columns: ['__CB__'],
      force: true
    })
  }

  get checkedIds() {
    const ids: any[] = [];
    this.gridApi.forEachNode((node: any) => {
      if (node.data.__grid_row_checked) {
        ids.push(this._getIdByRow(node.data))
      }
    })
    return ids
  }

  /**
   * 获取被勾选的行
   */
  getCheckedRows() {
    const selected: any[] = []

    this.gridApi.forEachNode((node: any) => {
      if (node.data.__grid_row_checked) {
        selected.push(node.data);
      }
    })
    return selected
  }

  /**
   * 获取被勾选的所有行
   */
  getAllCheckedRows() {
    if (this.useCrossPageCheck) {
      return this.allCheckedData
    }
    else {
      return this.getCheckedRows()
    }
  }

  /**
   * 获取被选中的行数据
   */
  getSelectedRow() {
    const rows = this.getSelectedRows()
    return rows.length > 0 ? rows[0] : undefined
  }

  /**
   * 获取被选中的行数据(多选情况下回返回多行数据)
   */
  getSelectedRows() {
    // 调用原生 getSelectedRows 方法有 bug
    // return this.gridApi.getSelectedRows();
    const selected: any[] = []
    this._findNode((node: any) => {
      if (node.selected) {
        selected.push(node.data)
      }
      return false
    })
    return selected
  }

  /**
   * 获取被选中的行
   */
  getSelectedNode() {
    const nodes = this.getSelectedNodes()
    return nodes.length > 0 ? nodes[0] : undefined
  }

  /**
   * 获取被选中的行(多选情况下回返回多行)
   */
  getSelectedNodes() {
    // 调用原生 getSelectedRows 方法有 bug
    // return this.gridApi.getSelectedRows();
    const selected: any[] = []
    this._findNode((node: any) => {
      if (node.selected) {
        selected.push(node)
      }
      return false
    })
    return selected
  }

  validate(): Promise<any> {
    const that = this
    return new Promise<any>((resolver, reject) => {
      const result: any[] = []
      const gridData = that.getGridExactlyData()
      _.forEach(that.columns, column => {
        if (column.editParams?.required === true) {
          _.forEach(gridData, (item, index) => {
            const val = _.get(item, column.field)
            if (!val && val != 0) {
              result.push({
                rowIndex: index,
                colKey: column.field,
                msg: column.title + '为必填项'
              })
            }
          })
        }
        if (column.editParams?.onValidate) {
          _.forEach(gridData, (item, index) => {
            const val = _.get(item, column.field)
            const res = YvEventDispatch(column.editParams?.onValidate, that, val)
            if (res) {
              result.push({
                rowIndex: index,
                colKey: column.field,
                msg: res
              })
            }
          })
        }
      })
      if (_.size(result) > 0) {
        that.gridApi.stopEditing()
        for (let ii = result.length - 1; ii >= 0; ii--) {
          const element = result[ii];
          that.gridApi.startEditingCell({
            rowIndex: element.rowIndex,
            colKey: element.colKey
          })
        }
        reject(result)
      } else {
        resolver(that.getGridExactlyData());
      }
    })
  }

  getGridData() {
    return this._gridData
  }

  getGridCreateData() {
    const dataE = this.getData()
    const data = _.filter(dataE, item => {
      return item.__grid_row_new == true && Object.keys(item).length > 1
    })
    return data
  }

  /**
   * 所有数据不包括空行
   */
  getGridExactlyData() {
    const data = this.getData()
    const rData: any[] = []
    _.forEach(data, item => {
      _.forOwn(item, (value, key) => {
        if (key != '__ID__' && key != '__CB__' && key != '__grid_row_checked' && key != '__grid_row_new' && value != null && value != '') {
          rData.push(item)
          return false
        }
      })
    })
    return rData
  }

  getAllEditRows() {
    const dataE = this.getData()
    const data = _.filter(dataE, item => {
      return (item.__grid_row_new == true && Object.keys(item).length > 1)|| item.__ID__
    })
    return data
  }

  /**
   * 所有新增数据不包括空行
   */
  getGridCreateExactlyData() {
    const data = this.getGridCreateData()
    const rData: any[] = []
    _.forEach(data, item => {
      _.forOwn(item, (value, key) => {
        if (key != '__ID__' && key != '__CB__' && key != '__grid_row_checked' && key != '__grid_row_new' && value != null && value != '') {
          rData.push(item)
          return false
        }
      })
    })
    return rData
  }
  /**
   * 判断数据是否为空
   * @param data 
   */
  isDataVoid(data: any) {
    let isVoid = true
    _.forOwn(data, (value, key) => {
      if (key != '__ID__' && key != '__CB__' && key != '__grid_row_checked' && key != '__grid_row_new' && value != null && value != '') {
        isVoid = false
        return false
      }
    })
    return isVoid
  }

  get hasCreateData() {
    const data: any = this.getGridCreateData()
    let isCreate = false
    if (!data || data.length <= 0) {
      return isCreate
    }
    data.forEach((element: any) => {
      _.forOwn(element, (value, key) => {
        if (key != '__ID__' && key != '__CB__' && key != '__grid_row_checked' && key != '__grid_row_new' && value != null && value != '') {
          isCreate = true
          return false
        }
      })
    });

    return isCreate
  }

  get hasData() {
    const data = this.getGridData()
    let isHas = false
    data.forEach(element => {
      _.forOwn(element, (value, key) => {
        if (key != '__ID__' && key != '__CB__' && key != '__grid_row_checked' && key != '__grid_row_new' && value != null && value != '') {
          isHas = true
          return false
        }
      })
    });
    return isHas
  }

  get gridEditCells() {
    if (!this._gridEditCells) {
      const vv: any[] = []
      _.forEach(this.vjson.columns, item => {
        if (item.editable) {
          vv.push(item.field)
        }
      })
      this._gridEditCells = vv
    }
    return this._gridEditCells
  }

  /**
   * 显示正在读取的状态
   */
  set loading(newValue: boolean) {
    if (!this.isGridReadReady) {
      return
    }
    if (newValue) {
      // 盖着
      this.gridApi.showLoadingOverlay()
      $($(this._webix.$view).find('.ag-paging-panel')[0]).append(`<div class="maskBox"></div>`);
    } else {
      // 放开
      $(this._webix.$view).find('.maskBox').remove()
      this.gridApi.hideOverlay()
    }
  }

  paginationSetPageSize(size: number) {
    this.gridApi.paginationSetPageSize(size)
  }

  /** 设置行号, 兼容分页 **/
  private setRowId(p: any) {
    return CtlGridIdRender(p, this)
  }

  /*============================ 私有属性部分 ============================*/
  private isGridReadReady: boolean = false
  private dataSourceBind?: YvanDataSourceGrid = undefined
  private _gridData: any[] = []
  private _gridEditCells: any

  private _getColumnDefs(): any {
    const resultCols: any[] = []

    //显示序号列
    if (this.showRowNumber) {
      resultCols.push({
        field: '__ID__',
        headerName: CtlGridLocale.rownumber,
        width: 52,
        //minWidth: 52,
        maxWidth: 160,
        pinned: 'left',
        resizable: true,
        sortable: false,
        cellRenderer: 'CtlGridIdRender'
      })
    }

    //显示勾选框
    if (this.checkbox) {
      resultCols.push({
        field: '__CB__',
        headerName: '',
        width: 40,
        minWidth: 40,
        maxWidth: 40,
        pinned: 'left',
        resizable: false,
        sortable: false,
        cellRenderer: 'CtlGridCellCheckbox',
        headerComponent: 'CtlGridHeadCheckbox',
        cellRendererParams: {
          isCheckedIds: true,
          checkboxDisabled: this.vjson.checkboxDisabled
        }
      })
    }

    //添加自定义列
    this._gridCols(resultCols)

    let columnDefs = resultCols
    if (!this.columnGroup || _.size(this.columnGroup) <= 0) {
      //没有多级表头
      columnDefs = resultCols
    } else {
      //二级表头
      columnDefs = []
      let j = 0
      let currentGroup: any
      let currentGroupSpan = -1

      for (let i = 0; i < resultCols.length; i++) {
        const f = resultCols[i]
        if (!this.columnGroup[j]) {
          columnDefs.push(f)
          continue
        }
        const { from, title, span, width } = this.columnGroup[j]
        if (currentGroupSpan > 0) {
          currentGroup.children.push(f)
          currentGroupSpan--
          if (currentGroupSpan <= 0) {
            j++
          }
        } else if (f.field === from) {
          currentGroup = {
            width: width,
            headerName: title,
            children: [f]
          }
          currentGroupSpan = span - 1
          columnDefs.push(currentGroup)
        } else {
          columnDefs.push(f)
        }
      }
    }

    _.forEach(columnDefs, item => {
      _.assign(item, {
        suppressKeyboardEvent: this.suppressDelete.bind(this)
      })
    })

    return columnDefs
  }

  private _gridOptions(): any {
    _.assign(this, {
      pagination: true,
      ..._.clone(YvGridProp),
      ...this.vjson,
    })

    // 动态列设置回调， 会返回一个异步Promise 或者 符合vjson 中columns定义的数组
    const re =YvEventDispatch(this.vjson.onBeforeColumnsRender, this, this.columns)
    if (re instanceof Promise) {
      this.columns = []
      re.then((c:any)=>{
        this.columns = c
        const newColumnsDef = this._getColumnDefs()
        this.gridApi.setColumnDefs(newColumnsDef)
      })
    } else if (re instanceof Array) {
      this.columns = re
    }
    const columnDefs = this._getColumnDefs()

    const gridOptions = {
      headerHeight: this.headerHeight,
      rowHeight: this.rowHeight,
      suppressRowHoverHighlight: true,
      columnDefs,
      animateRows: false,
      suppressCellSelection: !this.allowCellSelection,
      suppressRowClickSelection: !this.allowRowSelection,
      suppressColumnMoveAnimation: true,
      pagination: this.pagination,
      paginationPageSize: this.pageSize,
      localeText: CtlGridLocale,
      rowModelType: 'infinite',
      infiniteInitialRowCount: 200,
      //maxConcurrentDatasourceRequests: 2,
      maxBlocksInCache: 5,
      //cacheOverflowSize
      rowSelection: 'single',
      enableBrowserTooltips: true,
      //enableCellChangeFlash: true,
      singleClickEdit: this.editSingleClick,
      floatingFilter: false,
      stopEditingWhenGridLosesFocus: this.stopEditingWhenGridLosesFocus,
      getRowStyle: this._getRowStyle.bind(this),
      onFirstDataRendered: this._firstDataRendered.bind(this),

      onGridReady: this._gridReady.bind(this),
      // tabToNextCell: this._tabToNextCell.bind(this),
      // navigateToNextCell: this._navigateToNextCell.bind(this),
      onCellKeyDown: this._cellKeyDown.bind(this),
      onRowDoubleClicked: this._rowDoubleClicked.bind(this),
      onCellEditingStarted: this._cellEditingStarted.bind(this),
      onCellEditingStopped: this._cellEditingStopped.bind(this),
      onRowSelected: this._rowSelected.bind(this),
      onModelUpdated: this._modelUpdated.bind(this),
      onCellFocused: this._cellFocused.bind(this),
      onCellClicked: this._cellClicked.bind(this),
      onFilterChanged: this._filterChanged.bind(this),
      onSortChanged: this._sortChanged.bind(this),
      enterMovesDown: false,
      enterMovesDownAfterEdit: false,
      accentedSort: true,
      defaultColDef: {
        // flex: 1,
        wrapText: this.wrapText,
        autoHeight: true,
        // sortable: true,
        resizable: true,
      },
      onColumnResized: (params: any) => {
        params.api.resetRowHeights()
      },
      onColumnVisible: (params: any) => {
        params.api.resetRowHeights()
      },
      components: {
        CtlGridCellButton: CtlGridCellButton,
        CtlGridCellCheckbox: CtlGridCellCheckbox,
        CtlGridHeadCheckbox: CtlGridHeadCheckbox,
        CtlGridEditorText: CtlGridEditorText,
        CtlGridEditorNumber: CtlGridEditorNumber,
        CtlGridEditorCombo: CtlGridEditorCombo,
        CtlGridEditorDate: CtlGridEditorDate,
        CtlGridEditorSearch: CtlGridEditorSearch,
        CtlGridFilterSet: CtlGridFilterSet,
        CtlGridCellImage: CtlGridCellImage,
        CtlGridIdRender: this.setRowId.bind(this) //CtlGridIdRender.bind(this)
      }
    }

    if (this.clientFilter) {
      delete gridOptions.onFilterChanged
    }
    if (this.clientSort) {
      delete gridOptions.onSortChanged
    }

    if (_.isArray(this.dataSource)) {
      //有数据，按 client 模式加载数据
      _.assign(gridOptions, {
        rowModelType: 'clientSide',
        rowData: this.data,
        data: []
      })
    }

    if (this.pagination) {
      _.assign(gridOptions, {
        rowModelType: 'clientSide',
        rowData: [],
        data: []
      })
    }

    return gridOptions
  }

  private _filterChanged() {
    if (this.dataSourceBind) {
      if ((!_.isEqual(this.gridApi.getFilterModel(), this.dataSourceBind.lastFilterModel)) || this.refreshMode == GridRefreshMode.refreshAndClearFilter) {
        const reload = _.get(this.dataSourceBind, 'reload')
        if (typeof reload === 'function') {
          reload.call(this.dataSourceBind);
          this.reload()
        }
      }
      // console.log('_filterChanged', this.gridApi.getFilterModel());
    }
  }

  private _sortChanged() {
    if (this.dataSourceBind) {
      if ((!_.isEqual(this.gridApi.getSortModel(), this.dataSourceBind.lastSortModel)) || this.refreshMode == GridRefreshMode.refreshAndClearFilter) {
        const reload = _.get(this.dataSourceBind, 'reload')
        if (typeof reload === 'function') {
          reload.call(this.dataSourceBind);
          this.reload()
        }
      }
      // console.log('_sortChanged', this.gridApi.getSortModel());
    }
  }

  private _gridReady() {
    this.isGridReadReady = true
    /** 分页视图 **/
    this.gridPage = new CtlGridPage(this)
    this._rebindDataSource()
    if (_.has(this.vjson, "pagination") && this.vjson.pagination === false) {
      this.gridPage.hidden()
    }
  }

  private _resetGrid() {
    this.isGridReadReady = false
    const gridOptions = this._gridOptions()
    const $el = $(this._webix._viewobj).find('[role="yvGrid"]')[0]
    const grid: any = new agGrid.Grid($el, gridOptions)
    grid.gridOptions.api.vue = this
    this.gridApi = grid.gridOptions.api
    this.columnApi = grid.gridOptions.columnApi

    //去掉 ag-unselectable 使表格允许被选定
    if ($el) {
      $($el)
        .find('.ag-root.ag-unselectable')
        .removeClass('ag-unselectable')
    }

    if (this.vjson.collectData && !this.dataSource) {
      this._transactionUpdate([])
    }
  }

  updateColumnsDefine(columns: any[]) {
    this.columns = columns
    const newColumnsDef = this._getColumnDefs()
    this.gridApi.setColumnDefs(newColumnsDef)
  }

  suppressDelete(params: any) {
    var KEY_DELETE = 8;

    var event = params.event;
    var key = event.which;
    var suppress = key === KEY_DELETE;

    return suppress;
  }

  _rowDoubleClicked(e: any) {
    YvEventDispatch(this.onRowDblClick, this, e.data)
  }

  /**
   * 获取下拉框的数据选项
   */
  _getComboFilterData(easyuiCol: any) {
    if (easyuiCol.editMode === 'combo') {
      if (typeof easyuiCol.editParams.data === 'string') {
        if (YvanUI.dict.hasOwnProperty(easyuiCol.editParams.data)) {
          return YvanUI.dict[easyuiCol.editParams.data]
        } else if (YvanUI.formatter.hasOwnProperty(easyuiCol.editParams.data)) {
          return YvanUI.formatter[easyuiCol.editParams.data]
        } else {
          console.error(
            '没有发现全局函数 YvanUI.formatter[dict].' +
            easyuiCol.editParams.data
          )
        }
      } else if (easyuiCol.editParams.data.constructor === Array && easyuiCol.editParams.data.length > 0) {
        const editParams = easyuiCol.editParams
        return _.map(editParams.data, item => {
          return {
            id: item[editParams.idField],
            text: item[editParams.textField]
          }
        })
      }
      else if (easyuiCol.editParams.dataSource) {
        const dataSourceBind = new YvDataSource(this, easyuiCol.editParams.dataSource, this._comboDataSourceProcess.bind(this, easyuiCol))
        dataSourceBind.init()
        easyuiCol.editParams.__dataSourceBind = dataSourceBind
        return this._comboValidate.bind(this, easyuiCol)
      }
    }

    let formatter = easyuiCol.formatter
    if (!this.isDesignMode() && typeof easyuiCol.formatter === 'string') {
      // formatter 是字符串，从全局 YvanUI.formatter 找方法
      if (YvanUI.dict.hasOwnProperty(easyuiCol.formatter)) {
        formatter = YvanUI.dict[easyuiCol.formatter]
      } else if (YvanUI.formatter.hasOwnProperty(easyuiCol.formatter)) {
        easyuiCol.formatter = YvanUI.formatter[easyuiCol.formatter]
      } else {
        console.error(
          '没有发现全局函数 YvanUI.formatter[dict].' + easyuiCol.editParams.data
        )
      }
    }

    if (formatter && formatter.constructor === Array) {
      // formatter 是数组，就是下拉数据本身
      return formatter
    }
  }

  _comboDataSourceProcess(easyuiCol: any, data: any) {
    if (easyuiCol.editParams.__dataSourceBind) {
      easyuiCol.editParams.__dataSourceBind.destory()
      delete easyuiCol.editParams.__dataSourceBind
      delete easyuiCol.editParams.dataSource
    }
    if ((data && data.length > 0)) {
      easyuiCol.editParams.options = data
      easyuiCol.valueValidate = (value: any) => {
        const optionItem = _.find(data, item => {
          const id = _.toString(item['id'])
          return id && id === _.toString(value)
        })
        if (optionItem) {
          //找到text属性值
          return optionItem['text']
        }
        return value
      }
      this.gridApi.refreshCells({
        columns: [easyuiCol.field],
        force: true
      })
    }
  }

  _comboValidate(easyuiCol: any, value: any) {
    if (easyuiCol.valueValidate) {
      const vv = easyuiCol.valueValidate(value)
      return vv
    }
    return value
  }

  /**
   * 第一次数据被渲染之后
   */
  _firstDataRendered() {
    YvEventDispatch(this.onFirstDataRendered, this, undefined)
  }

  /**
   * 接受更新
   * 状态位显示 OK, 删除 origin 数据, 并闪烁当前行
   */
  _acceptChanges(node: any) {
    if (node.origin) {
      node.cstate = 'ok'
      delete node.origin
      this.flashRow(node)
      node.setDataValue('__ID__', _.uniqueId())
    }
  }

  /**
   * 接受更新
   * 状态位显示 OK, 删除 origin 数据, 并闪烁当前行
   */
  acceptAllChanges() {
    this.gridApi.forEachNode((node: any) => {
      if (node.origin) {
        node.cstate = 'ok'
        delete node.origin
        this.flashRow(node)
        node.setDataValue('__ID__', _.uniqueId())
      }
    })
  }

  /**
   * 清空所有状态，准备获取新数据
   * 当前编辑都应该清空, 勾选也应该清空
   */
  _clearCache() {
    // this.checkedIds = [];
    this.gridApi.forEachNode((node: any) => {
      if (_.has(node.data, '__grid_row_checked')) {
        node.data.__grid_row_checked = false
      }
    })
    this.gridApi.forEachNode((node: any) => {
      delete node.cstate
      delete node.origin
    })
  }

  /**
   * 根据行，获取主键 ID, 当主键不存在时 返回 undefined
   */
  _getIdByRow(row: any) {
    if (!row) return
    if (this.idField) {
      if (this.idField.constructor === Array) {
        //联合组件，用valueSep分割
        return _.map(this.idField, (f: any) => _.toString(row[f])).join(this.valueSep)
      }

      return row[this.idField]
    }
    console.error('表格没有设置主键！！')
    return undefined
  }

  /**
   * 重新绑定数据源
   */
  _rebindDataSource() {
    const innerMethod = () => {
      if (this.dataSourceBind) {
        this.dataSourceBind.destory()
        this.dataSourceBind = undefined
      }

      if (this._webix && this._module) {
        this.dataSourceBind = new YvanDataSourceGrid(this, this.dataSource)
      }
    }

    if (!this._module.loadFinished) {
      // onload 函数还没有执行（模块还没加载完）, 延迟调用 rebind
      _.defer(innerMethod)

    } else {
      // 否则实时调用 rebind
      innerMethod()
    }
  }

  /**
   * 每次 AJAX 请求完毕之后会回调这里
   */
  _bindingComplete() {
    YvEventDispatch(this.onBindingComplete, this, undefined)
    if (!this.getSelectedId()) {
      if(this.defaultUnselecteRow){//默认不选中第一行
      }else{// 默认必须选中一行
        this.selectRow(() => true);
      }
    }
  }

  /**
   * 找到某行的 node, （一旦匹配到 condition 就停止）
   */
  _findNode(condition: GridSelectCondition) {
    if (!condition) {
      //返回第一条被找到的数据
      condition = () => true
    } else if (typeof condition === 'string') {
      //以主键查找的方式
      const key = condition
      condition = (n: any) => {
        return this._getIdByRow(n.data) === key
      }
    } else if (typeof condition === 'object') {
      //就是 node 对象, 直接返回
      return condition
    }

    const me = this
    let findNode = undefined
    try {
      this.gridApi.forEachNode((node: any) => {
        if ((condition as any).call(me, node)) {
          findNode = node
          throw Error()
        }
      })
    } catch (e) { }
    return findNode
  }

  _cellKeyDown(param: any) {
    const KEY_LEFT = 37
    const KEY_UP = 38
    const KEY_RIGHT = 39
    const KEY_DOWN = 40
    const KEY_TAB = 9
    const KEY_DELETE = 8
    const KEY_ENTER = 13
    const KEY_ESC = 27
    //event.stopPropagation();
    //event.preventDefault();

    //通知外部
    const r = YvEventDispatch(this.onKeyDown, this, param)
    if (r === true) {
      //已经被自定义函数处理掉
      return
    }

    if (param.event.keyCode === KEY_ENTER) {
      param.event.stopPropagation()
      param.event.preventDefault()
    }
    else if (param.event.keyCode === KEY_DELETE) {
      if (this.gridApi.getEditingCells().length <= 0) {
        param.event.stopPropagation()
        param.event.preventDefault()
        if (this.vjson.collectData == true) {
          this._deleteToUpdate(this._gridData, param.rowIndex);
          YvEventDispatch(this.onRowDelete, this, this._gridData[param.rowIndex])
        }
        else {
          if (this.dataSourceBind) {
            this.dataSourceBind._deleteRow(param)
            YvEventDispatch(this.onRowDelete, this, param.data)
          }
        }
      }
    }
    else if (param.event.keyCode === KEY_ESC) {
      //按下 ESC 还原数据到 origin 状态, 并删除所有编辑形式
      if (param.node.origin) {
        param.event.stopPropagation()
        param.event.preventDefault()

        const data = {
          ...param.data,
          ...param.node.origin
        }
        delete param.node.cstate
        delete param.node.origin
        param.node.updateData(data)
        param.node.setDataValue('__ID__', _.uniqueId())
      }
    }
    else if (param.event.keyCode === KEY_UP) {
      param.event.stopPropagation()
      param.event.preventDefault()
      const vv = param.rowIndex - 1
      if (vv < 0) {
        return
      }
      this.selectRowWithCell(node => node.rowIndex === vv, param.colDef.field)
      this.gridApi.stopEditing()
      this.gridApi.startEditingCell({
        rowIndex: vv,
        colKey: param.colDef.field
      })
    }
    else if (param.event.keyCode === KEY_DOWN) {
      param.event.stopPropagation()
      param.event.preventDefault()
      const vv = param.rowIndex + 1
      if (vv >= this._gridData.length) {
        return
      }
      this.selectRowWithCell(node => node.rowIndex === vv, param.colDef.field)
      this.gridApi.stopEditing()
      this.gridApi.startEditingCell({
        rowIndex: vv,
        colKey: param.colDef.field
      })
    }
    else if (param.event.keyCode === KEY_LEFT || (param.event.keyCode === KEY_TAB && param.event.shiftKey === true)) {
      param.event.stopPropagation()
      param.event.preventDefault()
      const vv = this._getPreviousEditCellFields(param.colDef.field)
      if (vv) {
        this.gridApi.setFocusedCell(param.rowIndex, vv)
        this.gridApi.stopEditing()
        this.gridApi.startEditingCell({
          rowIndex: param.rowIndex,
          colKey: vv
        })
      }
      else {
        const num = param.rowIndex - 1
        if (num < 0) {
          return
        }
        this.singleSelectRow(node => node.rowIndex === num)
      }
    }
    else if (param.event.keyCode === KEY_RIGHT || param.event.keyCode === KEY_TAB) {
      param.event.stopPropagation()
      param.event.preventDefault()
      const vv = this._getNextEditCellFields(param.colDef.field)
      if (vv) {
        this.gridApi.setFocusedCell(param.rowIndex, vv)
        this.gridApi.stopEditing()
        this.gridApi.startEditingCell({
          rowIndex: param.rowIndex,
          colKey: vv
        })
      }
      else {
        const num = param.rowIndex + 1
        if (num >= this._gridData.length) {
          if (this.vjson.collectData == true) {
            if (this._keyTabRowValidate(param.node.id, param)) {
              param.node.cstate = 'error'
              param.node.setDataValue('__ID__', _.uniqueId())
              return
            }
            this._transactionUpdate(this._gridData)
            const firstField = this._getFirstEditCellFields();
            if (firstField) {
              this.singleSelectRow(node => node.rowIndex === num)
              this.gridApi.setFocusedCell(num, firstField)
              this.gridApi.startEditingCell({
                rowIndex: num,
                colKey: firstField
              })
            }
          }
          else {
            this._rowEditingStopped(param.node.id, param)
          }
          return
        }
        this.singleSelectRow(node => node.rowIndex === num)
      }
    }
  }

  _getPreviousEditCellFields(field: string) {
    const nn = _.indexOf(this.gridEditCells, field);
    if (nn == -1 && nn - 1 < 0) {
      return null
    }
    return this.gridEditCells[nn - 1]
  }

  _getNextEditCellFields(field: string) {
    const nn = _.indexOf(this.gridEditCells, field);
    if (nn == -1 && nn + 1 >= this.gridEditCells.length) {
      return null
    }
    return this.gridEditCells[nn + 1]
  }

  _getFirstEditCellFields() {
    if (this.gridEditCells.length >= 0) {
      return this.gridEditCells[0]
    }
    return null
  }

  _modelUpdated() {
    if (this.autoSizeColumns && this.gridApi) {
      this.gridApi.sizeColumnsToFit()
    }
  }

  _cellFocused(param: any) {
    YvEventDispatch(this.onCellFocused, this, param)
  }

  _cellClicked(param: any) {
    YvEventDispatch(this.onCellClicked, this, param)

    if (this.onRowCheck && param.colDef.field == "__CB__") {
      //  触发 onRowCheck 事件
      YvEventDispatch(this.onRowCheck, this, param.data)
    }
  }


  _getRowStyle(param: any) {
    return YvEventDispatch(this.onRowStyle, this, param);
    // 三种默认背景色：浅红色：return{background: '#ffd2d2'} 浅绿色：return{background: '#bde2c8'} 浅黄色：return{background: '#fdfbc1'}
  }

  _rowSelected(param: any) {
    const { node } = param
    const { selected, id } = node

    if (!selected) {
      //行离开事件,查看是否有数据正在编辑，提交校验
      this._rowEditingStopped(id, param)
      return
    }

    //触发 entity 改变
    if (_.size(this.entityName) > 0) {
      _.set(this.getModule(), this.entityName + '.selectedRow', param.data)
      //this.vcxt.module.$set(this.vcxt.module[this.entityName], "selectedRow", param.data);
    }

    //触发 onRowSelect 事件
    YvEventDispatch(this.onRowSelect, this, param.data)
  }

  _onChecked() {

  }

  _cellEditingStarted(param: any) {
    let rowId
    if (param.node.rowPinned === 'top') {
      //在添加行上
      rowId = -1
    } else if (!param.node.rowPinned) {
      //在数据行上
      rowId = param.node.id
    }
    this._rowEditingStarted(rowId, param)
  }

  _cellEditingStopped(param: any) {
    //触发单元格校验事件
    if (this.saveOn !== 'editFinish') {
      //保存时机，是不是结束编辑后立刻保存
      return
    }

    const origin = param.node.origin
    if (!origin) {
      // 这一行没有进入过编辑模式
      return
    }

    const data = _.cloneDeep(param.data)
    delete data['__ID__']
    delete data['__CB__']

    _.forOwn(origin, (value, key) => {
      if (typeof value === 'number') {
        origin[key] = _.toString(value)
      }
    })
    _.forOwn(data, (value, key) => {
      if (typeof value === 'number') {
        data[key] = _.toString(value)
      }
    })

    if (_.isEqual(origin, data)) {
      //相同，改变状态位 same
      param.node.cstate = 'same'
    } else {
      //不相同, 提交校验
      param.node.cstate = 'validate'

      if (this.dataSourceBind) {
        if (this.dataSourceBind.updateSupport()) {
          this.dataSourceBind._updateRow(param)
        }
      }
      if (this.vjson.cellEditingStopped) {
        YvEventDispatch(this.vjson.cellEditingStopped, this, param);
      }
      //console.log(this.dataSource, param.node)
      //setTimeout(() => {
      //    this._acceptChanges(param.node)
      //}, 2000)
    }
    param.node.setDataValue('__ID__', _.uniqueId())
  }

  _rowEditingStarted(rowId: any, param: any) {
    if (!param.node.origin) {
      // 以前从来没有编辑过这一行, 记录 origin
      const data = _.cloneDeep(param.data)
      delete data['__ID__']
      delete data['__CB__']
      param.node.origin = data
    }

    param.node.cstate = 'editing'
    param.node.setDataValue('__ID__', _.uniqueId())
  }

  _keyTabRowValidate(rowId: any, param: any) {
    let validateFaild = false
    const validateFaildFileds: any[] = []
    _.forEach(this.columns, column => {
      const currentVal = _.get(param.data, column.field)
      if (column.editParams?.required == true) {
        if (!currentVal && currentVal != 0) {
          validateFaildFileds.push(column.field)
          validateFaild = true
          return false
        }
      }
      if (column.editParams?.onValidate) {
        const currentResult = YvEventDispatchArgs(column.editParams?.onValidate, this, [currentVal, param.data])
        if (currentResult) {
          validateFaildFileds.push(column.field)
          validateFaild = true
          return false
        }
      }
    })
    if (validateFaild && validateFaildFileds.length > 0) {
      /**
       * todo validate不通过是否需要处理
       */
      // this.gridApi.stopEditing()
      // for (let ii = validateFaildFileds.length - 1; ii >= 0; ii--) {
      //   const element = validateFaildFileds[ii]
      //   this.gridApi.startEditingCell({
      //     rowIndex: parseInt(rowId),
      //     colKey: element
      //   })
      // }
    }
    return validateFaild
  }

  _getRowValidate(rowId: any, param: any) {
    let validateFaild = false
    _.forEach(this.columns, column => {
      // const currentVal = _.get(this.getSelectedRow(), column.field)
      const val = _.get(param.data, column.field)
      if (column.editParams?.required == true) {
        // if (!currentVal) {
        //   validateFaild = true
        //   return false
        // }
        if (!val && val != 0) {
          // this.selectRowWithCell(node => node.rowIndex === parseInt(rowId), column.field)
          // this.gridApi.stopEditing()
          // this.gridApi.startEditingCell({
          //   rowIndex: parseInt(rowId),
          //   colKey: column.field
          // })
          validateFaild = true
          return false
        }
      }
      if (column.editParams?.onValidate) {
        // const currentResult = YvEventDispatch(column.editParams?.onValidate, this, currentVal)
        const result = YvEventDispatchArgs(column.editParams?.onValidate, this, [val, param.data])
        // if (currentResult) {
        //   validateFaild = true
        //   return false
        // }
        if (result) {
          // this.selectRowWithCell(node => node.rowIndex === parseInt(rowId), column.field)
          // this.gridApi.stopEditing()
          // this.gridApi.startEditingCell({
          //   rowIndex: parseInt(rowId),
          //   colKey: column.field
          // })
          validateFaild = true
          return false
        }
      }
    })
    return validateFaild
  }

  _rowEditingStopped(rowId: any, param: any) {
    if (this.isDataVoid(param.data)) {
      // param.node.cstate = 'same'
      // param.node.setDataValue('__ID__', _.uniqueId())
      return
    }
    if (this._getRowValidate(rowId, param)) {
      param.node.cstate = 'error'
      param.node.setDataValue('__ID__', _.uniqueId())
      return
    }
    if (this.vjson.collectData == true) {
      return
    }
    if (this.saveOn !== 'rowChanged') {
      //保存时机，是不是行更改后立刻保存
      return
    }

    const origin = param.node.origin
    if (!origin) {
      // 这一行没有进入过编辑模式
      return
    }

    const data = _.cloneDeep(param.data)
    delete data['__ID__']
    delete data['__CB__']

    _.forOwn(origin, (value, key) => {
      if (typeof value === 'number') {
        origin[key] = _.toString(value)
      }
    })
    _.forOwn(data, (value, key) => {
      if (typeof value === 'number') {
        data[key] = _.toString(value)
      }
    })

    if (_.isEqual(origin, data)) {
      //相同，改变状态位 same
      param.node.cstate = 'same'
    } else {
      //不相同, 提交校验
      param.node.cstate = 'validate'

      if (this.dataSourceBind) {
        if (data.__grid_row_new) {
          this.dataSourceBind._addRow(param)
        }
        else {
          this.dataSourceBind._updateRow(param)
        }
      }
      //console.log(this.dataSource, param.node)
      //setTimeout(() => {
      //    this._acceptChanges(param.node)
      //}, 2000)
    }
    param.node.setDataValue('__ID__', _.uniqueId())
  }

  /**
   * Tab键导航
   */
  _tabToNextCell(params: any) {
    const previousCell = params.previousCellPosition
    const nextCellPosition = params.nextCellPosition

    //tab 永不换行
    return {
      ...nextCellPosition,
      rowIndex: previousCell.rowIndex
    }
  }

  /**
   * 上下左右键导航
   */
  _navigateToNextCell(params: any) {
    console.log('_navigateToNextCell', params)
    const KEY_LEFT = 37
    const KEY_UP = 38
    const KEY_RIGHT = 39
    const KEY_DOWN = 40

    const previousCell = params.previousCellPosition
    const suggestedNextCell = params.nextCellPosition
    switch (params.key) {
      case KEY_UP: {
        const nextRowIndex = previousCell.rowIndex - 1
        if (nextRowIndex < 0) {
          // returning null means don't navigate
          return null
        }
        this.selectRow(node => node.rowIndex === nextRowIndex)
        return {
          rowIndex: nextRowIndex,
          column: previousCell.column,
          floating: previousCell.floating
        }
      }

      case KEY_DOWN: {
        // return the cell below
        const rowIndex = previousCell.rowIndex + 1
        const renderedRowCount = this.gridApi.getModel().getRowCount()
        if (rowIndex >= renderedRowCount) {
          // returning null means don't navigate
          return null
        }
        this.selectRow(node => node.rowIndex === rowIndex)
        return {
          rowIndex: rowIndex,
          column: previousCell.column,
          floating: previousCell.floating
        }
      }

      case KEY_LEFT:
      case KEY_RIGHT:
        return suggestedNextCell
      default:
        throw 'this will never happen, navigation is always one of the 4 keys above'
    }
  }

  /**
   * 列设置计算
   */
  _gridCols(resultCols: any[]): void {
    _.each(this.columns, column => {
      const easyuiCol = _.merge(
        {
          ..._.clone(YvGridColumnProp),
          editParams: {
            ..._.clone(YvGridColumnEditProp)
          },
          wrapText: true,
          autoHeight: true,
        },
        column
      )

      //=========================== 设计模式属性 ===========================
      // if (this._isDesignMode) {
      //     resultCols.push({
      //         suppressMovable: true,
      //         field: easyuiCol.field,
      //         headerName: easyuiCol.title,
      //         resizable: true,
      //         filter: false,
      //         editable: false,
      //         sortable: false,
      //         //unSortIcon: true,
      //         hide: false,
      //         width: easyuiCol.width,
      //         minWidth: easyuiCol.minwidth,
      //         maxWidth: easyuiCol.maxwidth
      //     });
      //     return;
      // }

      //=========================== 基本属性 ===========================
      const col: any = {
        suppressMovable: true,
        field: easyuiCol.field,
        headerName: easyuiCol.title,
        resizable: easyuiCol.resizable,
        filter: false,
        editable: false,
        sortable: easyuiCol.sortable,
        //unSortIcon: true,
        hide: easyuiCol.hidden
      }
      if (easyuiCol.pinned === 'left' || easyuiCol.pinned === 'right') {
        col.pinned = easyuiCol.pinned
      }

      if (easyuiCol.flex && easyuiCol.flex > 0 ) {
        col.flex = easyuiCol.flex
      }

      if (easyuiCol.suppressSizeToFit && easyuiCol.suppressSizeToFit === true) {
        col.suppressSizeToFit = easyuiCol.suppressSizeToFit
      }

      if (easyuiCol.sortable) {
        // 走服务端排序，客户端排序可以让其无效
        col.comparator = () => {
          return 0;
        }
      }

      if (typeof easyuiCol.width !== 'undefined') col.width = easyuiCol.width
      if (typeof easyuiCol.minwidth !== 'undefined')
        col.minWidth = easyuiCol.minwidth
      if (typeof easyuiCol.maxwidth !== 'undefined')
        col.maxWidth = easyuiCol.maxwidth

      if (typeof easyuiCol.align !== 'undefined') {
        col.cellClass = function (params: any) {
          return ['yv-align-' + easyuiCol.align]
        }
      }

      if (_.size(easyuiCol.field) > 0) {
        col.tooltipField = easyuiCol.field
      }

      //=========================== buttons 属性 ===========================
      if (easyuiCol.buttons) {
        //col.cellRendererFramework = 'yvGridButton'
        col.cellRenderer = 'CtlGridCellButton'
        col.cellRendererParams = {
          buttons: easyuiCol.buttons
        }
      }
      if (easyuiCol.images) {
        col.cellRenderer = 'CtlGridCellImage'
        col.cellRendererParams = {
          images: easyuiCol.images
        }
      }

      //=========================== 编辑与formatter属性 ===========================
      const { editParams } = easyuiCol
      let formatable = false
      if (easyuiCol.editable && !this.isDesignMode()) {
        if (easyuiCol.editMode === 'checkbox') {
          //勾选框编辑
          formatable = false
          col.cellRenderer = 'CtlGridCellCheckbox'
          col.cellRendererParams = {
            editParams: easyuiCol.editParams,
            on: editParams.on,
            off: editParams.off,
            onChange: (newValue: any) => {
              YvEventDispatch(editParams.onValidate, this, {
                value: newValue
              })
            }
          }

          if (easyuiCol.editable) {
            //允许编辑
            _.assign(col, {
              editable: true,
              cellEditor: 'CtlGridEditorCombo',
              cellEditorParams: {
                editParams: easyuiCol.editParams,
                options: [
                  { id: editParams.on, text: '勾选' },
                  { id: editParams.off, text: '不勾' }
                ],
                onChange: (newValue: any) => {
                  YvEventDispatch(editParams.onValidate, this, {
                    value: newValue
                  })
                }
              }
            })
          }
        } else if (easyuiCol.editMode === 'combo') {
          //下拉框编辑
          formatable = false
          const data = this._getComboFilterData(easyuiCol)
          _.assign(col, {
            editable: true,
            cellEditor: 'CtlGridEditorCombo',
            cellEditorParams: {
              editParams: easyuiCol.editParams,
              options: typeof data === 'function' ? null : data,
              onChange: (newValue: any) => {
                YvEventDispatch(editParams.onChange, this, {
                  value: newValue
                })
              }
            }
          })
          //下拉框的 formatter 逻辑是固定的
          if (typeof data === 'function') {
            col.valueFormatter = (params: any) => {
              return data(params.value)
            }
          } else {
            col.valueFormatter = (params: any) => {
              if (_.size(params.data) <= 0) return

              const optionItem = _.find(data, item => {
                const id = _.toString(item['id'])
                return id && id === _.toString(params.value)
              })

              if (optionItem) {
                //找到text属性值
                return optionItem['text']
              }
              return params.value
            }
          }

        } else if (easyuiCol.editMode === 'area') {
          //大型富文本框编辑
          formatable = true
          _.assign(col, {
            editable: true,
            cellEditor: 'agLargeTextCellEditor',
            cellEditorParams: {
              editParams: easyuiCol.editParams,
              maxLength: editParams.maxlength
            }
          })

        } else if (easyuiCol.editMode === 'text') {
          //普通文本框编辑
          formatable = true
          _.assign(col, {
            editable: true,
            formatter: easyuiCol.formatter,
            cellEditor: 'CtlGridEditorText',
            cellEditorParams: {
              type: easyuiCol.editMode,
              editParams: easyuiCol.editParams,
              onChange: (newValue: any) => {
                YvEventDispatch(editParams.onValidate, this, {
                  value: newValue
                })
              }
            }
          })

        } else if (easyuiCol.editMode === 'number') {
          //普通文本框编辑 数字
          formatable = true
          _.assign(col, {
            editable: true,
            cellEditor: 'CtlGridEditorNumber',
            cellEditorParams: {
              type: easyuiCol.editMode,
              editParams: easyuiCol.editParams,
              onChange: (newValue: any) => {
                YvEventDispatch(editParams.onValidate, this, {
                  value: newValue
                })
              }
            }
          })
        } else if (easyuiCol.editMode === 'date' || easyuiCol.editMode === 'datetime') {
          formatable = true
          _.assign(col, {
            editable: true,
            formatter: easyuiCol.formatter,
            cellEditor: "CtlGridEditorDate",
            cellEditorParams: {
              type: easyuiCol.editMode,
              editParams: easyuiCol.editParams,
              onChange: (newValue: any) => {
                YvEventDispatch(editParams.onValidate, this, {
                  value: newValue
                })
              }
            }
          });
        }
        else if (easyuiCol.editMode === 'search') {
          formatable = true
          _.assign(col, {
            editable: true,
            widget: easyuiCol.widget,
            cellEditor: "CtlGridEditorSearch",
            cellEditorParams: {
              type: easyuiCol.editMode,
              editParams: easyuiCol.editParams,
              onChange: (newValue: any) => {
                YvEventDispatch(editParams.onValidate, this, {
                  value: newValue
                })
              }
            }
          });
        }
      } else {
        //不允许编辑的情况，全都允许格式化
        formatable = true
      }

      //=========================== formatter属性 ===========================
      if (formatable && !this.isDesignMode()) {
        const data = this._getComboFilterData(easyuiCol)
        if (data) {
          //从下拉框逻辑中找到了固定映射关系
          col.valueFormatter = (params: any) => {
            if (_.size(params.data) <= 0) return undefined
            const optionItem = _.find(data, item => {
              const id = _.toString(item['id'])
              return id && id === _.toString(params.value)
            })

            if (optionItem) {
              //找到text属性值
              return optionItem['text']
            }
            return params.value
          }
        } else {
          //以 function 方式获得显示逻辑
          let formatter = easyuiCol.formatter
          if (typeof easyuiCol.formatter === 'string') {
            // formatter 是字符串，从全局 YvanUI.formatter 找方法
            if (!YvanUI.formatter.hasOwnProperty(easyuiCol.formatter)) {
              console.error(
                '没有发现全局函数 YvanUI.formatter.' + easyuiCol.formatter
              )
            } else {
              formatter = YvanUI.formatter[easyuiCol.formatter]
            }
          }

          if (typeof formatter === 'function') {
            //formatter 是函数，调用函数来显示
            col.valueFormatter = (params: any) => {
              if (_.size(params.data) <= 0) return undefined
              return formatter.call(
                this,
                params.data[easyuiCol.field],
                this,
                params.data
              )
            }
          }
        }
      }

      //=========================== 过滤属性 ===========================
      if (this.filterable && easyuiCol.filterable && !easyuiCol.hidden) {
        const datas = this._getComboFilterData(easyuiCol)
        if (typeof datas === 'object') {
          //下拉框过滤
          _.assign(col, {
            filter: 'CtlGridFilterSet',
            //suppressMenu: true,
            filterParams: {
              data: datas,
            },
          })
        } else if (easyuiCol.editMode === 'number') {
          //数字过滤
          _.assign(col, {
            filter: 'agNumberColumnFilter', //NumberFilter = 26850
            //suppressMenu: true,
            filterParams: {
              applyButton: true,
              clearButton: true,
              suppressAndOrCondition: true,
              filterOptions: [
                // 服务器已经设置条件，浏览器不进行实际比对
                { displayKey: 'equals', displayName: '等于', test() { return true; } },
                { displayKey: 'notEqual', displayName: '不等于', test() { return true; } },
                { displayKey: 'lessThan', displayName: '小于', test() { return true; } },
                { displayKey: 'greaterThan', displayName: '大于', test() { return true; } },
                { displayKey: 'lessThanOrEqual', displayName: '小于等于', test() { return true; } },
                { displayKey: 'greaterThanOrEqual', displayName: '大于等于', test() { return true; } },
                { displayKey: 'inRange', displayName: '范围', test() { return true; } },
              ]
            }
          })

        } else if (easyuiCol.editMode === 'date' || easyuiCol.editMode === 'datetime') {
          //日期筛选
          _.assign(col, {
            filter: 'agDateColumnFilter',
            filterParams: {
              applyButton: true,
              clearButton: true,
              filterOptions: ['inRange'],
              suppressAndOrCondition: true,
              inRangeInclusive: true,
              comparator(v1: any, v2: any) {
                // 服务器已经设置条件，浏览器不进行实际比对
                // 0 的情况，一定要包含 inRangeInclusive 条件
                return 0
              }
            }
          })
        } else {
          //其他情况都是字符串筛选
          _.assign(col, {
            filter: 'agTextColumnFilter',
            filterParams: {
              applyButton: true,
              clearButton: true,
              filterOptions: ['contains', 'equals', 'startsWith'],
              suppressAndOrCondition: true,
              textCustomComparator() {
                // 服务器已经设置条件，浏览器不进行实际比对
                return true;
              }
            }
          })
        }
      }

      //=========================== 渲染属性 ===========================
      if (!this.isDesignMode()) {
        if (typeof easyuiCol.onStyle === 'function') {
          _.assign(col, {
            cellStyle: (param: any) => {
              return easyuiCol.onStyle.call(this, param)
            }
          })
        }
        if (easyuiCol.onStyle?.type && easyuiCol.onStyle?.bind) {
          _.assign(col, {
            cellStyle: (param: any) => {
              return YvEventDispatch(easyuiCol.onStyle, this, {
                "field": param.colDef.field,
                "value": param.value,
                "data": param.data
              })
            }
          })
        }
      }

      if (easyuiCol.editable && easyuiCol.editModeByDataField) {
        delete col.cellEditor
        col.editModeByDataField = easyuiCol.editModeByDataField
        _.assign(col, {
          cellEditorSelector: this.getColEditMode.bind(this)
        })
      }
      resultCols.push(col)
    })
  }

  getColEditMode(params: any) {
    const type = _.get(params.data, params.colDef.editModeByDataField)
    if (type === 'checkbox') {
      return {
        component: 'CtlGridCellCheckbox',
      };
    }
    else if (type === 'combo') {
      return {
        component: 'CtlGridEditorCombo',
      };
    }
    if (type === 'area') {
      return {
        component: 'agLargeTextCellEditor',
      };
    }
    else if (type === 'text' || type === 'string') {
      return {
        component: 'CtlGridEditorText',
      };
    }
    else if (type === 'number') {
      return {
        component: 'CtlGridEditorNumber',
      };
    }
    else if (type === 'date' || type === 'datetime') {
      return {
        component: 'CtlGridEditorDate'
      };
    }
    else if (type === 'search') {
      return {
        component: 'CtlGridEditorSearch'
      };
    }
    else {
      throw new Error('unsupported edit type')
    }
  }
}

export type GridSelectCondition = ((row: any) => boolean) | string
